﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using GeneratorLib.DB;
using GeneratorLib.DB.Connectios;
using GeneratorLib.DB.Schemas;


namespace Generator.TemplateGenerators
{

    public class TemplateToCode
    {

        private string _templateSource;

        public string DebugCode { get; private set; }
        public string ResultCode { get; private set; }

        public TableInfo Table { get; set; }

        public string TemplateSource
        {
            get
            {
                return _templateSource;
            }
            set
            {
                _templateSource = value;
                initConfig();
            }
        }

        public TemplateConfig_Template TempalteConfig
        {
            get;
            private set;
        }

        public TemplateConfig_Output OutputConfig
        {
            get;
            private set;
        }

        public TemplateToCode(TableInfo table, string templateSource)
        {
            this.Table = table;
            this.TemplateSource = templateSource;
            DebugCode = "";
            ResultCode = "";
        }

        private void initConfig()
        {
            TempalteConfig = new TemplateConfig_Template();
            OutputConfig = new TemplateConfig_Output();

            TemplateConfigParser parse = new TemplateConfigParser();
            using (System.IO.StringReader sr = new System.IO.StringReader(TemplateSource))
            {
                while (sr.Peek() > 0)
                {
                    try
                    {
                        parse.ParseCode(sr.ReadLine());
                    }                   
                    catch (Exception ex)
                    {
                        if (ex is ConfigReadFinishedException)
                        {                           
                           foreach (var d in parse.Configs)
                           {
                               if (d.Key.CompareTo("template") == 0)
                               {
                                   TempalteConfig = new TemplateConfig_Template(this.Table,d.Value);
                               } 
                               else if (d.Key.CompareTo("output") == 0)
                               {
                                   OutputConfig = new TemplateConfig_Output(this.Table, d.Value);
                               }
                           }
                           break;
                        }
                    }
                }
            }

        }

        public void ConvertToCSharp()
        {

            DebugCode = "";
            ResultCode = "";
            
            StringBuilder sbCSharp = new StringBuilder();
            TemplateLineParser parse = new TemplateLineParser();
            int lineNo = 0;
            using (System.IO.StringReader sr = new System.IO.StringReader(TemplateSource))
            {
                while (sr.Peek() > 0)
                {
                    lineNo++;
                    string s = parse.ConvertCode(sr.ReadLine(), lineNo);
                    if (s.Length > 0)
                    {
                        sbCSharp.Append(s);
                    }
                }
            }

            DebugCode = sbCSharp.ToString();
            Generator.TemplateGenerators.Context cxt = new Generator.TemplateGenerators.Context();
            try
            {
                ResultCode = cxt.ToCsharpCode(Table, DebugCode);
            }
            catch(Exception ex)
            {
                ResultCode = ex.ToString();
            }
        }

        public bool CheckSyntax()
        {
            bool revalue = true;
            Regex reg1 = new Regex(@"(<#@)|(<#=)|(<#)|(#>)");
            MatchCollection matches = reg1.Matches(TemplateSource);
            //check
            if (matches.Count % 2 != 0)
            {
                revalue = false;
            }
            if (revalue)
            {
                for (int i = 0; i < matches.Count; i++)
                {
                    if (!matches[i].Value.StartsWith("<#"))
                    {
                        revalue = false;
                        break;
                    }
                    i++;
                    if (!matches[i].Value.StartsWith("#>"))
                    {
                        revalue = false;
                        break;
                    }
                }
            }

            return revalue;
        }



    }


    public class TemplateLineParser
    {


        public string StartWith { get; private set; }
        public bool symbolOpened
        {
            get
            {
                return StartWith.Length > 0;
            }
        }
        //public Dictionary<string, Dictionary<string, string>> Configs
        //{
        //    get;
        //    private set;
        //}

        public TemplateLineParser()
        {
            StartWith = "";
            //Configs = new Dictionary<string, Dictionary<string, string>>();
        }

        public string ConvertCode(string s,int lineNo)
        {

            //if ( s.TrimStart().StartsWith("<#"))
            //{
            //    if (!s.TrimStart().StartsWith("<#"))
            //        s = s.TrimStart();
               
            //}

            //s= s.Replace("\"", "\\" + "\"");
            
            int position = 0;
            StringBuilder sbCode = new StringBuilder();

            Regex reg1 = new Regex(@"(<#\s*@)|(<#\s*=)|(<#)|(#>)");
            MatchCollection matches = reg1.Matches(s);

            if (matches.Count == 0 && !symbolOpened)
            {
                sbCode.AppendLine(string.Format("codeBuilder.AppendLine(deSerialStr(\"{0}\"));", serialStr(s)));
            }
            else if (matches.Count == 0 && symbolOpened)
            {
                if (StartWith.Replace(" ", "") != "<#@")
                    sbCode.AppendLine(string.Format("{0}", s));
                //else
                //    sbConfig.AppendLine(string.Format("{0}", s));
            }
            else
            {
                for (int i = 0; i < matches.Count; i++)
                {
                    Match match = matches[i];
                    if (match.Value.StartsWith("<#"))
                    {

                        if ((match.Index - position) > 0)
                        {
                            if (match.Value.Replace(" ", "").Length == 2 && sbCode.Length == 0 && s.Substring(position, match.Index - position).Trim().Length==0)
                            {
                            
                            }
                            else
                            {
                                sbCode.AppendLine(string.Format("codeBuilder.Append(deSerialStr(\"{0}\"));", serialStr(s.Substring(position, match.Index - position))));
                            }
                        }

                        StartWith = match.Value;
                        position = match.Index + match.Length;

                        Match nextMatch = match.NextMatch();
                        if (nextMatch == null || nextMatch.Success == false || nextMatch.Length == 0)
                        {
                            switch (StartWith.Replace(" ", ""))
                            {
                                case "<#@":
                                    //sbConfig.AppendLine(s.Substring(position));
                                    //AddReadedConfig(TemplateConfigParser.Parse(sbConfig.ToString()));
                                    //sbConfig.Clear();
                                    break;
                                case "<#=":
                                    if (s.Substring(position).Trim().Length>0)
                                        sbCode.AppendLine(string.Format("codeBuilder.AppendLine(string.Format(\"{{0}}\",{0}));", s.Substring(position)));
                                    break;
                                case "<#":
                                    sbCode.AppendLine(s.Substring(position));
                                    break;
                            }

                            position = s.Length;
                        }

                    }
                    else if (match.Value.StartsWith("#>"))
                    {
                        bool isend = (s.Length <= (match.Index + match.Length));
                        if (!isend)
                        {
                            if (s.Substring(match.Index + match.Length).Trim().Length == 0)
                            {
                                isend = true;
                            }
                        }
                        string AppendType = isend ? "AppendLine" : "Append";
                        switch (StartWith.Replace(" ", ""))
                        {
                            case "<#@":
                                //sbConfig.AppendLine(s.Substring(position, match.Index - position));
                                //AddReadedConfig(TemplateConfigParser.Parse(sbConfig.ToString()));
                                //sbConfig.Clear();
                                break;
                            case "<#=":
                                sbCode.AppendLine(string.Format("codeBuilder.{1}(string.Format(\"{{0}}\",{0}));", s.Substring(position, match.Index - position), AppendType));
                                break;
                            case "<#":
                                if (match.Index > 0)
                                {
                                    sbCode.AppendLine(s.Substring(position, match.Index - position));
                                    //if (isend  )
                                    //    sbCode.AppendLine("codeBuilder.AppendLine();");
                                }
                                break;
                        }
                        position = match.Index + match.Length;
                        StartWith = "";
                    }
                }

                //Match lastMatch= matches[matches.Count-1];
                if (position < s.Length)
                {
                    if (s.Substring(position).Trim().Length > 0)
                        sbCode.AppendLine(string.Format("codeBuilder.AppendLine(deSerialStr(\"{0}\"));", serialStr(s.Substring(position))));
                    //else
                    //    sbCode.AppendLine(); //"codeBuilder.AppendLine();");

                }
                //sbCode.AppendLine("codeBuilder.Append(Environment.NewLine);");
            }

            return sbCode.ToString();
        }

        private string serialStr(string str)
        {
            return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(str));
        }

        private string deSerialStr(string str)
        {
            return System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(str));
        }

        //private void AddReadedConfig(Dictionary<string, Dictionary<string, string>> config)
        //{
        //    if (config == null) return;
        //    foreach (var dic in config)
        //    {
        //        Configs.Add(dic.Key, dic.Value);
        //    }
        //}


    }

    public class TemplateConfigParser
    {
        public string StartWith { get; private set; }
        StringBuilder sbConfig = new StringBuilder();

        public bool symbolOpened
        {
            get
            {
                return StartWith.Length > 0;
            }
        }
        public Dictionary<string, Dictionary<string, string>> Configs
        {
            get;
            private set;
        }

        public TemplateConfigParser()
        {
            StartWith = "";
            Configs = new Dictionary<string, Dictionary<string, string>>();
        }

        public void ParseCode(string s)
        {

            if (s.TrimStart().StartsWith("<#"))
            {
                s = s.TrimStart();
            }

            int position = 0;

            Regex reg1 = new Regex(@"(<#\s*@)|(<#\s*=)|(<#)|(#>)");
            MatchCollection matches = reg1.Matches(s);

            if (matches.Count == 0 && symbolOpened)
            {
                if (StartWith.Replace(" ", "") == "<#@")
                {
                    sbConfig.Append(string.Format("{0}", s));                    
                }
                else
                {
                    throw new ConfigReadFinishedException();
                }
            }
            else
            {
                for (int i = 0; i < matches.Count; i++)
                {
                    Match match = matches[i];
                    if (match.Value.StartsWith("<#"))
                    {
                        StartWith = match.Value;
                        position = match.Index + match.Length;

                        Match nextMatch = match.NextMatch();
                        if (nextMatch == null || nextMatch.Success == false || nextMatch.Length == 0)
                        {
                            switch (StartWith.Replace(" ", ""))
                            {
                                case "<#@":
                                    sbConfig.Append(s.Substring(position));                                    
                                    break;
                                case "<#=":
                                    throw new ConfigReadFinishedException();
                                case "<#":
                                    throw new ConfigReadFinishedException();
                            }

                            position = s.Length;
                        }

                    }
                    else if (match.Value.StartsWith("#>"))
                    {
                        switch (StartWith.Replace(" ", ""))
                        {
                            case "<#@":
                                sbConfig.Append(s.Substring(position, match.Index - position));
                                AddReadedConfig(Parse(sbConfig.ToString()));
                                sbConfig.Clear();
                                break;
                            case "<#=":
                                 throw new ConfigReadFinishedException();
                            case "<#":
                                 throw new ConfigReadFinishedException();
                        }
                        position = match.Index + match.Length;
                        StartWith = "";
                    }
                }
            }

        }

        private void AddReadedConfig(Dictionary<string, Dictionary<string, string>> config)
        {
            if (config == null) return;
            foreach (var dic in config)
            {
                Configs.Add(dic.Key, dic.Value);
            }
        }


        public Dictionary<string, Dictionary<string, string>> Parse(string config)
        {

            string pattern1 = string.Format("{0}+{1}+(?={0}+=\"[^\"|]*\"{1}+)", @"\w", @"\s");
            Regex regex1 = new Regex(pattern1);
            Match match1 = regex1.Match(config);
            if (!match1.Success)
            {
                return null;
            }

            string pattern2 = string.Format("{0}+(?={1}*=)|(?<=={1}*\"){1}*[^\"]*{1}*(?=\")", @"\w", @"\s");
            Regex regex2 = new Regex(pattern2);
            MatchCollection matches = regex2.Matches(config);
            if (matches.Count == 0 || matches.Count % 2 != 0)
            {
                return null;
            }

            Dictionary<string, Dictionary<string, string>> root = new Dictionary<string, Dictionary<string, string>>();
            Dictionary<string, string> cfg = new Dictionary<string, string>();
            for (int i = 0; i < matches.Count; i++)
            {
                Match m = matches[i];
                cfg.Add(m.Value.Trim().ToLower(), m.NextMatch().Value.Trim());
                i++;
            }

            root.Add(match1.Value.Trim().ToLower(), cfg);

            return root;
        }
    }

    public class ConfigReadFinishedException : Exception
    {
        public ConfigReadFinishedException()
        { }
    }

    /*
     <#@ template language="c#" HostSpecific="True" #>
     */
    public class TemplateConfig_Template
    {
        public string Language { get; private set; }
        public string FileType { get; private set; }
        
        public TableInfo Table { get; set; }

        public TemplateConfig_Template()
        {
        }
        public TemplateConfig_Template(TableInfo table, Dictionary<string, string> dic)
        {
            init();
            this.Table = table;
            foreach (var v in dic)
            {
                if (v.Key.ToLower().CompareTo("language") == 0)
                {
                    Language = v.Value.Trim();
                }
                else if (v.Key.ToLower().CompareTo("filetype") == 0)
                {
                    FileType = v.Value.Trim();
                }
            }
        }
        private void init()
        { 
            Language = "c#";
            FileType = "c#";
            this.Table = null;
        }
    }

     /*
    <#@ output language="html" extension= ".aspx" #>
     */
   public class TemplateConfig_Output
    {
        public string Language { get; private set; }
        public string FileType { get; private set; }
        public string Extension { get; private set; }
        public string AutoSavePath { get; private set; }
        public string AutoSaveFileName { get; private set; }

        public TableInfo Table { get; set; }

        public string GetAutoSavePath()
        {
            return getDynaName(this.Table, AutoSavePath);
        }

        public string GetAutoSaveFileName()
        {
            return getDynaName(this.Table, AutoSaveFileName);
        }

        public TemplateConfig_Output()
        {
            init();
        }

        private void init()
        { 
            Language = "c#";
            Extension = ".cs";
            FileType = "c#";
            AutoSavePath = "{TableHost.Table.EntityName}";
            AutoSaveFileName = "{TableHost.Table.EntityName}.cs";
            this.Table = null;
        }

        public TemplateConfig_Output(TableInfo table, Dictionary<string, string> dic)
        {
            init();
            this.Table = table;
            foreach (var v in dic)
            {
                if (v.Key.ToLower().CompareTo("language") == 0)
                {
                    Language = v.Value.Trim();
                }
                else if (v.Key.ToLower().CompareTo("filetype") == 0)
                {
                    FileType = v.Value.Trim();
                }
                else if (v.Key.ToLower().CompareTo("extension") == 0)
                {
                    Extension = v.Value.Trim();
                }
                else if (v.Key.ToLower().CompareTo("autosavepath") == 0)
                {
                    AutoSavePath = v.Value.Trim();
                }
                else if (v.Key.ToLower().CompareTo("autosavefilename") == 0)
                {
                    AutoSaveFileName = v.Value.Trim();
                }
            }
        }

        private string getDynaName(TableInfo table,string inParam)
        {
            if (table == null) return inParam;

            string pattern = @"{\s*[\w.]+\s*}";
            Regex regex = new Regex(pattern);
            MatchCollection matches = regex.Matches(inParam);
            if (matches.Count == 0)
                return inParam;

            StringBuilder sb1 = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();
            int position = 0;
            int i = 0;
            foreach (Match  m in matches)
            {
                sb1.Append(inParam.Substring(position, m.Index - position));
                sb1.AppendFormat  ("{1}{0}{2}",i,"{","}");
                string t= m.Value.Substring(1);
                t= t.Substring(0,t.Length -1);
                sb2.AppendFormat("{0}{1}", ",", t.Trim());
                position = m.Index + m.Length;
                i++;
            }

            if (position < inParam.Length)
            {
                sb1.Append(inParam.Substring(position));
            }

            string code= string.Format("string.Format(\"{0}\"{1});", sb1.ToString().Replace(@"\",@"\\"), sb2);

            Generator.TemplateGenerators.Context cxt = new Generator.TemplateGenerators.Context();
            try
            {
                return cxt.GetTemplateConfig(table, code);
            }
            catch
            {
                return inParam;
            }
        }
    }


   public class TemplateFile
   {
       public string FileName { get; private set; }
       public string TemplateName { get; private set; }

       public TemplateFile(string fileName)
       {
           this.FileName = fileName;
           if (fileName.Length > 0)
           {
               System.IO.FileInfo fi = new System.IO.FileInfo(fileName);
               TemplateName = fi.Name.Replace(fi.Extension, "");
           }
           else
           {
               TemplateName = "";
           }
           
       }

       public override string ToString()
       {
           return TemplateName;
       }
   }
}
